{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "15ba0126-56d3-426e-977f-aee8a94646a6",
   "metadata": {},
   "source": [
    "# Planning Pattern - ReAct Technique"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d71b47fa-4265-49ef-8e1f-cf8249e12a3f",
   "metadata": {},
   "source": [
    "<img src=\"../img/planning_pattern.png\" alt=\"Alt text\" width=\"500\"/>\n",
    "\n",
    "---\n",
    "\n",
    "So, we've seen agents capable of reflecting and using tools to access the outside world. But ... **what about planning**,\n",
    "i.e. deciding what sequence of steps to follow to accomplish a large task?\n",
    "\n",
    "That is exactly what the Planning Pattern provides; ways for the LLM to break a task into **smaller, more easily accomplished subgoals** without losing track of the end goal.\n",
    "\n",
    "The most paradigmatic example of the planning pattern is the [**ReAct**](https://react-lm.github.io/) technique, displayed in the diagram above.\n",
    "\n",
    "In this notebook, you'll learn how this technique actually works. This is the **third lesson** of the \"Agentic Patterns from Scratch\" series. Take a look\n",
    "at the previous lessons if you haven't!\n",
    "\n",
    "* [First Lesson: The Reflection Pattern](https://github.com/neural-maze/agentic_patterns/blob/main/notebooks/reflection_pattern.ipynb)\n",
    "* [Second Lesson: The Tool Pattern](https://github.com/neural-maze/agentic_patterns/blob/main/notebooks/tool_pattern.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd0dc83a-f11b-469d-bb2c-afbd91f39c5e",
   "metadata": {},
   "source": [
    "## Relevant imports and Groq Client"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "605420e2-4bab-4d0a-90ac-7b9a95fd9976",
   "metadata": {},
   "source": [
    "We start by importing all the libraries we'll be using in this tutorial as well as the Groq client."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "056fc0ed-7ded-490b-ae0b-e7d1fd71430c",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import re\n",
    "import math\n",
    "import json\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "from groq import Groq\n",
    "\n",
    "from agentic_patterns.tool_pattern.tool import tool\n",
    "from agentic_patterns.utils.extraction import extract_tag_content\n",
    "\n",
    "\n",
    "# Remember to load the environment variables. You should have the Groq API Key in there :)\n",
    "load_dotenv()\n",
    "\n",
    "MODEL = \"llama-3.1-70b-versatile\"\n",
    "GROQ_CLIENT = Groq()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46f4363c-b6e0-4c4a-aa08-ad243ddf7911",
   "metadata": {},
   "source": [
    "> If you are not familiar with the `tool` decorator, changes are you are missed the previous tutorial about the Tool Pattern. Check the video [here](https://www.youtube.com/watch?v=ApoDzZP8_ck&t=671s&ab_channel=TheNeuralMaze)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "af6735f3-00d3-4a41-acb8-f4afb4a759d4",
   "metadata": {},
   "source": [
    "## A System Prompt for the ReAct Loop"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69b721b1-cedf-4e61-9d0b-0c090972efce",
   "metadata": {},
   "source": [
    "As we did with the Tool Pattern, we also need a System Prompt for the ReAct technique. This System Prompt is very similar, the difference is that it describes the ReAct loop, so that the LLM is aware of\n",
    "the three operations it's allowed to use:\n",
    "\n",
    "1. Thought: The LLM will think about which action to take\n",
    "2. Action: The LLM will use a Tool to \"act on the environment\"\n",
    "3. Observation: The LLM will observe the tool output and reflect on the next thing to do.\n",
    "\n",
    "Another key difference from the Tool Pattern System Prompt is that we are going to enclose all the messages with tags, like these: <thought></thought>, <observation></observation>. We could implement the ReAct logic without these tags, but I found it eeasier for the LLM to understand the instructions this way.\n",
    "\n",
    "Ok! So without further ado, there's the prompt!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "87279781-38d4-45df-b8b5-e41c587ba38b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define the System Prompt as a constant\n",
    "REACT_SYSTEM_PROMPT = \"\"\"\n",
    "You are a function calling AI model. You operate by running a loop with the following steps: Thought, Action, Observation.\n",
    "You are provided with function signatures within <tools></tools> XML tags.\n",
    "You may call one or more functions to assist with the user query. Don' make assumptions about what values to plug\n",
    "into functions. Pay special attention to the properties 'types'. You should use those types as in a Python dict.\n",
    "\n",
    "For each function call return a json object with function name and arguments within <tool_call></tool_call> XML tags as follows:\n",
    "\n",
    "<tool_call>\n",
    "{\"name\": <function-name>,\"arguments\": <args-dict>, \"id\": <monotonically-increasing-id>}\n",
    "</tool_call>\n",
    "\n",
    "Here are the available tools / actions:\n",
    "\n",
    "<tools> \n",
    "%s\n",
    "</tools>\n",
    "\n",
    "Example session:\n",
    "\n",
    "<question>What's the current temperature in Madrid?</question>\n",
    "<thought>I need to get the current weather in Madrid</thought>\n",
    "<tool_call>{\"name\": \"get_current_weather\",\"arguments\": {\"location\": \"Madrid\", \"unit\": \"celsius\"}, \"id\": 0}</tool_call>\n",
    "\n",
    "You will be called again with this:\n",
    "\n",
    "<observation>{0: {\"temperature\": 25, \"unit\": \"celsius\"}}</observation>\n",
    "\n",
    "You then output:\n",
    "\n",
    "<response>The current temperature in Madrid is 25 degrees Celsius</response>\n",
    "\n",
    "Additional constraints:\n",
    "\n",
    "- If the user asks you something unrelated to any of the tools above, answer freely enclosing your answer with <response></response> tags.\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3db5712-97dd-424c-bdf1-5da0970da03a",
   "metadata": {},
   "source": [
    "## Example step by step"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ddd00354-7ebc-44ec-9606-0a178af59b44",
   "metadata": {},
   "source": [
    "### Defining the Tools"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4c6360cd-b9f2-4427-91bc-27f010147c04",
   "metadata": {},
   "source": [
    "Let's build an example that involves the use of three tools, like the following ones."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c1d3bec1-679d-4d80-bf41-bf54b00e985c",
   "metadata": {},
   "outputs": [],
   "source": [
    "@tool\n",
    "def sum_two_elements(a: int, b: int) -> int:\n",
    "    \"\"\"\n",
    "    Computes the sum of two integers.\n",
    "\n",
    "    Args:\n",
    "        a (int): The first integer to be summed.\n",
    "        b (int): The second integer to be summed.\n",
    "\n",
    "    Returns:\n",
    "        int: The sum of `a` and `b`.\n",
    "    \"\"\"\n",
    "    return a + b\n",
    "\n",
    "\n",
    "@tool\n",
    "def multiply_two_elements(a: int, b: int) -> int:\n",
    "    \"\"\"\n",
    "    Multiplies two integers.\n",
    "\n",
    "    Args:\n",
    "        a (int): The first integer to multiply.\n",
    "        b (int): The second integer to multiply.\n",
    "\n",
    "    Returns:\n",
    "        int: The product of `a` and `b`.\n",
    "    \"\"\"\n",
    "    return a * b\n",
    "\n",
    "@tool\n",
    "def compute_log(x: int) -> float | str:\n",
    "    \"\"\"\n",
    "    Computes the logarithm of an integer `x` with an optional base.\n",
    "\n",
    "    Args:\n",
    "        x (int): The integer value for which the logarithm is computed. Must be greater than 0.\n",
    "\n",
    "    Returns:\n",
    "        float: The logarithm of `x` to the specified `base`.\n",
    "    \"\"\"\n",
    "    if x <= 0:\n",
    "        return \"Logarithm is undefined for values less than or equal to 0.\"\n",
    "    \n",
    "    return math.log(x)\n",
    "\n",
    "\n",
    "available_tools = {\n",
    "    \"sum_two_elements\": sum_two_elements,\n",
    "    \"multiply_two_elements\": multiply_two_elements,\n",
    "    \"compute_log\": compute_log\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "98c3044e-5d5c-44d0-973b-0c7c31f9724b",
   "metadata": {},
   "source": [
    "Remember that the `@tool` operator allows us to convert a Python function into a `Tool` automatically. We cana check that very easily with some of the functions above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2b7e2078-9c5d-4687-8c1b-56b36a0194db",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Tool name: \", sum_two_elements.name)\n",
    "print(\"Tool signature: \", sum_two_elements.fn_signature)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "292a747b-f48a-4c36-b62f-b528466c53a8",
   "metadata": {},
   "source": [
    "### Adding the Tools signature to the System Prompt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "67cabcdb-4dc4-49b3-9a9a-fc79e1ad12b0",
   "metadata": {},
   "source": [
    "Now, we just concatenate the tools signature and add them to the System Prompt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b464ea29-400c-482a-83f8-d64fba727ff8",
   "metadata": {},
   "outputs": [],
   "source": [
    "tools_signature = sum_two_elements.fn_signature + \",\\n\" + multiply_two_elements.fn_signature + \",\\n\" + compute_log.fn_signature"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "501dd7c6-3ef2-46fb-bc01-a888692b8fa5",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(tools_signature)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b52ea466-79c0-4898-9c0b-69a07c2ad1db",
   "metadata": {},
   "outputs": [],
   "source": [
    "REACT_SYSTEM_PROMPT = REACT_SYSTEM_PROMPT % tools_signature"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "40801b47-0021-4800-8364-9c515db1e419",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(REACT_SYSTEM_PROMPT)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2b1fd6c-253a-4a40-ab29-4d68fa976ac1",
   "metadata": {},
   "source": [
    "### ReAct Loop Step 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cb771338-8865-4e37-8b8b-02a949f67c41",
   "metadata": {},
   "outputs": [],
   "source": [
    "USER_QUESTION = \"I want to calculate the sum of 1234 and 5678 and multiply the result by 5. Then, I want to take the logarithm of this result\"\n",
    "chat_history = [\n",
    "    {\n",
    "        \"role\": \"system\",\n",
    "        \"content\": REACT_SYSTEM_PROMPT\n",
    "    },\n",
    "    {\n",
    "        \"role\": \"user\",\n",
    "        \"content\": f\"<question>{USER_QUESTION}</question>\"\n",
    "    }\n",
    "]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "61faa368-e936-4bf2-9a88-df2a35bb205e",
   "metadata": {},
   "outputs": [],
   "source": [
    "output = GROQ_CLIENT.chat.completions.create(\n",
    "    messages=chat_history,\n",
    "    model=MODEL\n",
    ").choices[0].message.content\n",
    "\n",
    "print(output)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9514f9ab-c162-44f6-bc51-247061f811de",
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_history.append(\n",
    "    {\n",
    "        \"role\": \"assistant\",\n",
    "        \"content\": output\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cf7ce6ab-7325-4844-b4d1-1ba590d8aba9",
   "metadata": {},
   "source": [
    "### ReAct Loop Step 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5a207669-3ced-43ee-8aad-c8ebf347f797",
   "metadata": {},
   "outputs": [],
   "source": [
    "tool_call = extract_tag_content(output, tag=\"tool_call\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4614fe15-efa3-4111-b4e7-eb04e160f5f0",
   "metadata": {},
   "outputs": [],
   "source": [
    "tool_call"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3b7a6279-8cd6-4d18-8a9d-90ea04e552ed",
   "metadata": {},
   "outputs": [],
   "source": [
    "tool_call = json.loads(tool_call.content[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f5835594-0105-4452-9aac-7ca76fafaf6c",
   "metadata": {},
   "outputs": [],
   "source": [
    "tool_call"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "34259246-b8b6-4ae9-aa4b-1bf705fdb3d0",
   "metadata": {},
   "outputs": [],
   "source": [
    "tool_result = available_tools[tool_call[\"name\"]].run(**tool_call[\"arguments\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8724515b-4ba8-46a6-83fc-be472c2f1bd0",
   "metadata": {},
   "outputs": [],
   "source": [
    "assert tool_result == 1234 + 5678"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a8196b58-708b-4c9c-a090-eb4ad213a927",
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_history.append(\n",
    "    {\n",
    "        \"role\": \"user\",\n",
    "        \"content\": f\"<observation>{tool_result}</observation>\"\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "608684c2-18c3-4e13-885e-a8f1b5e40caa",
   "metadata": {},
   "source": [
    "### ReAct Loop Step 3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4eb27636-9080-44bd-991c-5f89f96e2675",
   "metadata": {},
   "outputs": [],
   "source": [
    "output = GROQ_CLIENT.chat.completions.create(\n",
    "    messages=chat_history,\n",
    "    model=MODEL\n",
    ").choices[0].message.content\n",
    "\n",
    "print(output)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "34603c55-b114-4ef7-be3a-ef0148eaddb8",
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_history.append(\n",
    "    {\n",
    "        \"role\": \"assistant\",\n",
    "        \"content\": output\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c855578-49e3-4bff-981c-16659ec1b4a4",
   "metadata": {},
   "source": [
    "### ReAct Loop Step 4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8d494dab-7e1d-44e8-a91d-685731d76864",
   "metadata": {},
   "outputs": [],
   "source": [
    "tool_call = extract_tag_content(output, tag=\"tool_call\")\n",
    "tool_call = json.loads(tool_call.content[0])\n",
    "tool_result = available_tools[tool_call[\"name\"]].run(**tool_call[\"arguments\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "33420c29-428e-4392-9c52-57b77d2fcc32",
   "metadata": {},
   "outputs": [],
   "source": [
    "tool_result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0b61013e-be7a-457e-8a57-329266b29cc0",
   "metadata": {},
   "outputs": [],
   "source": [
    "assert tool_result == (1234 + 5678) * 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d422e9d2-773b-461a-b04b-5b0de30d59e5",
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_history.append(\n",
    "    {\n",
    "        \"role\": \"user\",\n",
    "        \"content\": f\"<observation>{tool_result}</observation>\"\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7b66d90d-6386-4026-8ea5-aa37a143c21c",
   "metadata": {},
   "source": [
    "### ReAct Loop Step 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9c5beee0-8684-4948-8e34-084dcab98eb0",
   "metadata": {},
   "outputs": [],
   "source": [
    "output = GROQ_CLIENT.chat.completions.create(\n",
    "    messages=chat_history,\n",
    "    model=MODEL\n",
    ").choices[0].message.content\n",
    "\n",
    "print(output)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "297ce32b-7460-477a-bd5f-2ddd8a45bf11",
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_history.append(\n",
    "    {\n",
    "        \"role\": \"assistant\",\n",
    "        \"content\": output\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4c1a336d-2795-41b4-88af-6fed9cebea60",
   "metadata": {},
   "source": [
    "### ReAct Loop Step 6"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "97eeb277-1486-4d8f-8441-8adbde84389e",
   "metadata": {},
   "outputs": [],
   "source": [
    "tool_call = extract_tag_content(output, tag=\"tool_call\")\n",
    "tool_call = json.loads(tool_call.content[0])\n",
    "tool_result = available_tools[tool_call[\"name\"]].run(**tool_call[\"arguments\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d77818e5-ae29-42d4-8c6e-4527bcf56959",
   "metadata": {},
   "outputs": [],
   "source": [
    "tool_result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "694284bb-64de-4f48-b0e2-c870b129a225",
   "metadata": {},
   "outputs": [],
   "source": [
    "assert tool_result == math.log((1234 + 5678) * 5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c26c8ae7-a6f3-4d8c-b509-fb39054684bf",
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_history.append(\n",
    "    {\n",
    "        \"role\": \"user\",\n",
    "        \"content\": f\"<observation>{tool_result}</observation>\"\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "72c74a9e-edbe-487a-9cda-17260006e639",
   "metadata": {},
   "source": [
    "### ReAct Loop Step 7"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d7faf46c-2ee2-4f40-b371-7a42a8885e8f",
   "metadata": {},
   "outputs": [],
   "source": [
    "output = GROQ_CLIENT.chat.completions.create(\n",
    "    messages=chat_history,\n",
    "    model=MODEL\n",
    ").choices[0].message.content\n",
    "\n",
    "print(output)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4960a1e6-7b49-48fe-b4f6-b6e54d9c03b8",
   "metadata": {},
   "source": [
    "## Doing the same but with `agentic_patterns` library"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6a4cd7f2-cc28-4d8a-a227-8f1346a3b7a8",
   "metadata": {},
   "outputs": [],
   "source": [
    "from agentic_patterns.planning_pattern.react_agent import ReactAgent"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f2462f18-f4ed-494e-8676-454e883ecc84",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent = ReactAgent(tools=[sum_two_elements, multiply_two_elements, compute_log])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3bd048fc-1415-4ea1-a9b5-f488fc9a1ef2",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent.run(user_msg=\"I want to calculate the sum of 1234 and 5678 and multiply the result by 5. Then, I want to take the logarithm of this result\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "817c78d6-760f-4161-b08b-7be9bf1fe010",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "We did it!! A ReAct Agent working as expected, completely from Scratch! 🚀🚀🚀🚀"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
